// $Id: fastapi.cpp,v 1.10 2009/03/03 22:59:24 nsharma Exp $
//
// FIX Adapted for STreaming (sm) (FAST Protocol (sm)) 
//
// Copyright (c) 2005-2006, Pantor Engineering AB (http://www.pantor.com)
// Copyright (c) 2005-2006, SpryWare LLC (http://www.spryware.com)
// Copyright (c) 2005-2006, FIX Protocol Ltd (http://www.fixprotocol.org)
// All Rights Reserved.
//
// This work is distributed under the W3C® Software License [1] in the
// hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS 
// FOR A PARTICULAR PURPOSE.
//
// [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
// [FPL's Intellectual Property details] http://www.fixprotocol.org/ip
// [FAST Protocol details] http://www.fixprotocol.org/fast
// [FAST Protocol credits] http://fixprotocol.org/fastcredits

#include "common.h"
#include "FASTApi.h"
#include <iostream>
#include <vector>
#include <string.h>

//////////////////////////////////////////////////////////////////////

void FASTApi::reset_buffer(FASTBuffer* buffer) {
	buffer->head = buffer->data;
	buffer->tail = buffer->data;
	buffer->end = buffer->data + sizeof(buffer->data);

}

void FASTApi::init_buffer(FASTBuffer* buffer, int fd) {
	reset_buffer(buffer);

	buffer->fd = fd;
}

int FASTApi::empty_buffer_pointer(FASTBuffer* buffer) {
	return buffer->head >= buffer->tail;
}

int FASTApi::get_buffer_left(FASTBuffer* buffer) {
	return (int) (buffer->end - buffer->tail);
}

int FASTApi::get_buffer_size(FASTBuffer* buffer) {
	return (int) (buffer->end - buffer->data);
}

int FASTApi::get_buffer_used(FASTBuffer* buffer) {
	return int(buffer->tail - buffer->head);
}

int FASTApi::set_codec_input(u8* pbuffer, unsigned int size) {
	p_codec->input.head = pbuffer;
	p_codec->input.tail = pbuffer + size;
	return size;
}

int FASTApi::get_codec_output(u8** pbuffer, unsigned int* psize) {
	*pbuffer = p_codec->output.head;
	*psize = get_buffer_used(&p_codec->output);
	return *psize;
}

//////////////////////////////////////////////////////////////////////

u32 FASTApi::get_tag_operator(u32 tag) {
	return (tag >> TAG_SHIFT_OP) & TAG_MAX_OP;
}

u32 FASTApi::get_tag_required(u32 tag) {
	return (tag >> TAG_SHIFT_REQ) & TAG_MAX_REQ;
}

u32 FASTApi::get_tag_slot(u32 tag) {
	return (tag >> TAG_SHIFT_SLOT) & TAG_MAX_SLOT;
}

u32 FASTApi::get_tag_tid(u32 tag) {
	return (tag >> TAG_SHIFT_TID) & TAG_MAX_TID;
}

u32 FASTApi::get_tag_type(u32 tag) {
	return (tag >> TAG_SHIFT_TYPE) & TAG_MAX_TYPE;
}

FASTCV* FASTApi::get_tag_state(u32 tag) {
	u32 tid = get_tag_tid(tag);
	return &p_codec->cv[tid];
}

i32* FASTApi::get_i32_value(u32 tag) {
	return get_tag_state(tag)->i32_values;
}

u32* FASTApi::get_u32_value(u32 tag) {
	return get_tag_state(tag)->u32_values;
}

i64* FASTApi::get_i64_value(u32 tag) {
	return get_tag_state(tag)->i64_values;
}

u64* FASTApi::get_u64_value(u32 tag) {
	return get_tag_state(tag)->u64_values;
}

double* FASTApi::get_double_value(u32 tag) {
	return get_tag_state(tag)->d_values;
}

u8** FASTApi::get_string_value(u32 tag) {
	return reinterpret_cast<u8**> (get_tag_state(tag)->str_values);
}

u32* FASTApi::get_valid_value(u32 tag) {
	return get_tag_state(tag)->valid;
}

//////////////////////////////////////////////////////////////////////

void FASTApi::get(u32 tag, i32 &value) {
	value = get_i32_value(tag)[get_tag_slot(tag)];
}

void FASTApi::get(u32 tag, u32 &value) {
	value = get_u32_value(tag)[get_tag_slot(tag)];
}

void FASTApi::get(u32 tag, i64 &value) {
	value = get_i64_value(tag)[get_tag_slot(tag)];
}

void FASTApi::get(u32 tag, u64 &value) {
	value = get_u64_value(tag)[get_tag_slot(tag)];
}

double FASTApi::get_double(u32 tag) {
	return get_double_value(tag)[get_tag_slot(tag)];
}

u8* FASTApi::get_string(u32 tag) {
	u8** str_values = get_string_value(tag);
	u32 slot = get_tag_slot(tag);

	if (str_values[slot] == NULL) {
		str_values[slot] = reinterpret_cast<u8*> (malloc(1024));
		assert(str_values[slot] != NULL);
		strcpy((char*) str_values[slot], "");
	}

	return str_values[slot];
}

//////////////////////////////////////////////////////////////////////

void FASTApi::set_valid(u32 tag) {
	get_valid_value(tag)[get_tag_slot(tag)] = 1;
}

int FASTApi::is_valid(u32 tag) {
	//check is valid OR optional
	return get_valid_value(tag)[get_tag_slot(tag)] != 0
			|| get_tag_required(tag) == false;
}

//////////////////////////////////////////////////////////////////////

int FASTApi::is_equal(u32 tag, i32 data) {
	i32 value;
	get(tag, value);
	return is_valid(tag) && value == data;
}

int FASTApi::is_equal(u32 tag, u32 data) {
	u32 value;
	get(tag, value);
	return is_valid(tag) && value == data;
}

int FASTApi::is_equal(u32 tag, i64 data) {
	i64 value;
	get(tag, value);
	return is_valid(tag) && value == data;
}

int FASTApi::is_equal(u32 tag, u64 data) {
	u64 value;
	get(tag, value);
	return is_valid(tag) && value == data;
}

int FASTApi::is_equal(u32 tag, u8* data, int size) {
	u8* cp = get_string(tag);

	// Invalid or not the same length
	// Fixme: cp [size] != '\0' not a valid test?
	if (is_valid(tag) == 0 || cp[size] != '\0')
		return 0;

	return memcmp(cp, data, size) == 0;
}

//////////////////////////////////////////////////////////////////////

//! @brief Set the current signed integer value for the specified tag

void FASTApi::set(u32 tag, i32 data) {
	get_i32_value(tag)[get_tag_slot(tag)] = data;
	set_valid(tag);
}

//////////////////////////////////////////////////////////////////////

//! @brief Set the current signed integer 64 value for the specified tag

//! @brief Set the current unsigned integer 64 value for the specified tag
void FASTApi::set(u32 tag, i64 data) {
	get_i64_value(tag)[get_tag_slot(tag)] = data;
	set_valid(tag);
}

void FASTApi::set(u32 tag, u32 data) {
	get_u32_value(tag)[get_tag_slot(tag)] = data;
	set_valid(tag);
}

void FASTApi::set(u32 tag, u64 data) {
	get_u64_value(tag)[get_tag_slot(tag)] = data;
	set_valid(tag);
}
//! @brief Set the current unsigned integer value for the specified tag

//! @brief Set the current double value for the specified tag
void FASTApi::set(u32 tag, double data) {
	get_double_value(tag)[get_tag_slot(tag)] = data;
	set_valid(tag);
}
//! @brief Set the current string value for the specified tag

void FASTApi::set(u32 tag, u8* data, int size) {
	u8* cp = get_string(tag);

	memcpy(cp, data, size);
	cp[size] = '\0';

	set_valid(tag);
}
//////////////////////////////////////////////////////////////////////

int FASTApi::toSize(u32 data) {
	const double base = 2;
	const int mfactor = 7;

	int i = 0;
	for (i = 1; i < 10; i++) {
		if (data < (u32) pow(base, mfactor * i))
			return i;
	}
	return 5;
}

int FASTApi::toSize(u64 data) {
	const double base = 2;
	const int mfactor = 7;

	int i = 0;
	for (i = 1; i < 10; i++) {
		if (data < (u64) pow(base, mfactor * i))
			return i;
	}

	return 9;
}

int FASTApi::toSize(i32 data) {
	const double base = 2;
	const int mfactor = 7;
	if (data < 0) {
		int i = 0;
		for (i = 1; i < 10; i++) {
			if (data >= -(i32) pow(base, mfactor * i - 1))
				return i;
		}
	} else {
		int i = 0;
		for (i = 1; i < 10; i++) {
			if (data < (i32) pow(base, mfactor * i - 1))
				return i; // -64
		}
	}
	return 5;
}

int FASTApi::toSize(i64 data) {
	const double base = 2;
	const int mfactor = 7;
	if (data < 0) {
		int i = 0;
		for (i = 1; i < 10; i++) {
			if (data >= -(i64) pow(base, mfactor * i - 1))
				return i;
		}
	} else {
		int i = 0;
		for (i = 1; i < 10; i++) {
			if (data < (i64) pow(base, mfactor * i - 1))
				return i;
		}
	}
	return 9;
}

//////////////////////////////////////////////////////////////////////

const char* FASTApi::forma_error_code(eFAST_ERROR code) {
	switch (code) {
	case eFAST_ERR_NONE:
		return "none";
	case eFAST_ERR_CODEC:
		return "bad codec";
	case eFAST_ERR_SIZE:
		return "size error";
	case eFAST_ERR_VALUE:
		return "missing value";
	case eFAST_ERR_TAG_OP:
		return "tag op unsupported";
	case eFAST_ERR_TAG_TYPE:
		return "bad tag type";
	case eFAST_ERR_CALL_SEQ:
		return "bad call sequence";
	case eFAST_ERR_IO:
		return "I/O library error";
	default:
		return "??";
	}
}

const char* FASTApi::format_tag_operator(eFAST_OP op) {
	static char buffer[32];

	switch (op) {
	case eFAST_OP_NONE:
		return "NONE";
	case eFAST_OP_CONST:
		return "CONST";
	case eFAST_OP_DEFAULT:
		return "DEFAULT";
	case eFAST_OP_COPY:
		return "COPY";
	case eFAST_OP_INCR:
		return "INCR";
	case eFAST_OP_DELTA:
		return "DELTA";
	case eFAST_OP_TAIL:
		return "TAIL";

	default:
		snprintf(buffer, sizeof(buffer), "%u", op);
		return buffer;
	}
}

const char* FASTApi::format_tag_type(eFAST_TYPE type) {
	static char buffer[32];

	switch (type) {
	case eFAST_TYPE_U32:
		return "U32";
	case eFAST_TYPE_I32:
		return "I32";
	case eFAST_TYPE_I64:
		return "I64";
	case eFAST_TYPE_U64:
		return "U64";
	case eFAST_TYPE_DECMIAL:
		return "D64";
	case eFAST_TYPE_ASCII:
		return "STR";
	case eFAST_TYPE_UNICODE:
		return "USTR";
	case eFAST_TYPE_BYTEVECTOR:
		return "BYTEVECTOR";
	case eFAST_TYPE_COMPONENT:
		return "COMPONENT";
	case eFAST_TYPE_GROUP:
		return "GROUP";

	default:
		snprintf(buffer, sizeof(buffer), "%u", type);
		return buffer;
	}
}

//////////////////////////////////////////////////////////////////////

int FASTApi::set_error(const char* fn, fast_tag_t tag, eFAST_ERROR code,
		const char* format, ...) {
	char buffer[1024];
	va_list ap;

	p_codec->error->fn = fn;
	p_codec->error->tag = tag;
	p_codec->error->code = code;

	va_start(ap, format);
	vsnprintf(buffer, sizeof(buffer), format, ap);
	va_end(ap);

	if (p_codec->error->text != NULL)
		free(p_codec->error->text);

	p_codec->error->text = reinterpret_cast<char*> (malloc(strlen(buffer) + 1));
	strcpy(p_codec->error->text, buffer);

	if (p_codec->verbose >= 1)
		fprintf(stderr, "error: %s\n", error_string());

	return -1;
}

int FASTApi::check_codec(const char* fn) {
	if (p_codec == NULL) {
		fprintf(stderr, "error: [%s] NULL codec\n", fn);
		return -1;
	}
	if (p_codec->magic != FAST_CODEC_MAGIC)
		return set_error(fn, -1, eFAST_ERR_CODEC, "bad codec magic number");

	return 0;
}

int FASTApi::check_type(const char* fn, fast_tag_t tag, u32 type) {
	if (get_tag_type(tag) != type)
		return set_error(fn, tag, eFAST_ERR_TAG_TYPE, "type=%s (%s expected)",
				format_tag_type(static_cast<eFAST_TYPE> (get_tag_type(tag))),
				format_tag_type(static_cast<eFAST_TYPE> (type)));

	return 0;
}

int FASTApi::bad_operator_error(const char* fn, fast_tag_t tag) {
	return set_error(fn, tag, eFAST_ERR_TAG_OP, "");
}

int FASTApi::value_error(const char* fn, fast_tag_t tag) {
	return set_error(fn, tag, eFAST_ERR_VALUE, "");
}

//! @brief Generate an error string using the current error info

const char* FASTApi::error_string() {
	fast_tag_t tag = p_codec->error->tag;

	static char buffer[1024];

	snprintf(buffer, sizeof(buffer), "[%s] %s|%s|%u|%u %s: %s",
			p_codec->error->fn, format_tag_type(
					static_cast<eFAST_TYPE> (get_tag_type(tag))),
			format_tag_operator(static_cast<eFAST_OP> (get_tag_operator(tag))),
			get_tag_tid(tag), get_tag_slot(tag), forma_error_code(
					p_codec->error->code),
			p_codec->error->text ? p_codec->error->text : "");

	return buffer;
}

int FASTApi::print_error(FILE* fp) {
	const char* msg = error_string();

	fprintf(fp, "%s\n", msg);
	return 0;
}

//////////////////////////////////////////////////////////////////////

//! @brief Get a presence bit from the current presence map

int FASTApi::get_presence_pap(fast_tag_t tag, u8 pset) {
	u32 pos = get_tag_slot(tag);
	if (pset) {
		return p_codec->pmap.bits[p_codec->pmap.max_pos++];
	}
	return p_codec->pmap.bits[p_codec->pmap.max_pos];
}

//! @brief Reset the current presence map

void FASTApi::reset_presence_map() {
	// Fixme: Consider memset'ing to max_pos.
	memset(p_codec->pmap.bits, 0, MAX_PMAP_BITS);
	p_codec->pmap.max_pos = 0;
}

int FASTApi::set_presence_map(fast_tag_t tag, u8 bset) {
	if (need_presence_map(tag)) {
		if (p_codec->pmap.max_pos >= MAX_PMAP_BITS)
			throw set_error(FUNCTION, tag, eFAST_ERR_SIZE,
					"pmap slot=%d (< %d expected)", p_codec->pmap.max_pos,
					MAX_PMAP_BITS);
		p_codec->pmap.bits[p_codec->pmap.max_pos++] = bset;
	}
	return 0;
}

int FASTApi::set_presence_map_x(fast_tag_t tag, eFAST_TYPE data_type) {
	if (get_tag_required(tag) == false) {
		if (data_type == eFAST_TYPE_ASCII || data_type == eFAST_TYPE_UNICODE) {
			//set '\0'
			u8 byte = '\0';
			emit_string(tag, &byte, 1);
		} else {
			//eFAST_TYPE_I32,eFAST_TYPE_I64
			//eFAST_TYPE_I64, eFAST_TYPE_U32
			//eFAST_TYPE_GROUP, eFAST_TYPE_DECMIAL
			//set 0
			int byte = 0;
			emit_byte((byte & VALUE_BIT) | STOP_BIT);
		}
		set_presence_map(tag, ePMAP_BIT_ON);
	} else {
		set_presence_map(tag, ePMAP_BIT_OFF);
	}
	return 0;
}

void FASTApi::backup_group(FASTGroup &group) {
	memset(&group, 0, sizeof(FASTGroup));
	memcpy(&group.pmap, &p_codec->pmap, sizeof(group.pmap));
	u32 iSize = get_buffer_used(&p_codec->msg);
	memcpy(&group.data, &p_codec->msg.data, iSize);
	group.iSize = iSize;
	reset_presence_map();
	reset_buffer(&p_codec->msg);
}

void FASTApi::restore_group(FASTGroup &group) {
	FASTGroup curr_group;
	backup_group(curr_group);
	memcpy(&p_codec->pmap, &group.pmap, sizeof(group.pmap));
	int prevSize = group.iSize;
	memcpy(p_codec->msg.tail, &group.data, prevSize);
	p_codec->msg.tail += prevSize;
	//flush the group

	int map_size;

	//! @remark Make sure there is room in the output buffer

	int size = curr_group.iSize;
	int need = MAX_PMAP_BYTES + size;

	if (get_buffer_left(&p_codec->msg) <= need) {
		set_error(FUNCTION, -1, eFAST_ERR_SIZE,
				"output buffer overflow left=%d need=%d", get_buffer_left(
						&p_codec->msg), need);
		return;
	}

	//! Copy the pmap into the output buffer
	map_size = emit_presence_map(&curr_group.pmap, p_codec->msg.tail);

	p_codec->msg.tail += map_size;

	//! Copy the message body into the output buffer
	memcpy(p_codec->msg.tail, curr_group.data, size);

	p_codec->msg.tail += size;

	return;

	//memcpy(&group.pmap, p_codec->pmap, sizeof(group.pmap));
	//memcpy(&group.msg, p_codec->msg, sizeof(p_codec->msg));
}

void FASTApi::backup_presence_map(FASTPMap &pmap) {
	// Fixme: Consider memset'ing to max_pos.
	memcpy(&pmap, &p_codec->pmap, sizeof(FASTPMap));
	return;
}

void FASTApi::restore_presence_map(FASTPMap pmap) {
	// Fixme: Consider memset'ing to max_pos.
	memcpy(&p_codec->pmap, &pmap, sizeof(FASTPMap));
	return;
}

bool FASTApi::need_presence_map(fast_tag_t tag) {
	if (get_tag_operator(tag) == eFAST_OP_NONE || get_tag_operator(tag)
			== eFAST_OP_DELTA || (get_tag_operator(tag) == eFAST_OP_CONST
			&& get_tag_required(tag) == true))
		return false;
	return true;
}

//! @brief Set a presence bit in the current presence map

static u32 bitMask[8] = { 64, 32, 16, 8, 4, 2, 1, 0 };

// emit_presence_map copies the pmap into the data buffer
// and returns the size of the pmap

//! Serialize the current presence map

int FASTApi::emit_presence_map(FASTPMap *pmap, u8* data) {
	int bytes = 0;
	if (pmap->max_pos % 7 == 0)
		bytes = align7(pmap->max_pos); // get map size in bytes
	else
		bytes = align7(pmap->max_pos + 1); // get map size in bytes
	int offset;
	int p1;

	// pack slots from map->bits (seven at a time) into
	// data to form a STOP bit coded field.

	for (p1 = 0, offset = 0; p1 < bytes; p1++, offset += 7) {
		u8* bits = &pmap->bits[offset];
		u32 temp = 0;
		int p2;

		for (p2 = 0; p2 < 7; p2++) // pack 7 slot into temp
		{
			if (bits[p2] > 0)
				temp |= bitMask[p2];
		}

		data[p1] = temp;
	}
	for (p1 = bytes; p1 > 0; p1--) // avoid LongValue
	{
		if (data[p1 - 1] != 0)
			break;
	}
	data[p1 - 1] |= STOP_BIT; // Set the STOP bit
	return p1;
}

//////////////////////////////////////////////////////////////////////

//! @brief Append a byte to the message buffer

void FASTApi::emit_byte(u8 data) {
	*p_codec->msg.tail++ = data;
}

//////////////////////////////////////////////////////////////////////

//! @brief Verify that there is enough room in the output buffer
//! to store size bytes of data into the buffer.

int FASTApi::check_message_available(const char* fn, fast_tag_t tag, int size) {
	if (get_buffer_left(&p_codec->msg) < size)
		return set_error(fn, tag, eFAST_ERR_SIZE,
				"message buffer overflow avail=%d needed=%d", get_buffer_left(
						&p_codec->msg), size);
	return 0;
}

//! @callgraph

//! @brief Serialize a 32-bit signed integer field
template<typename T>
int emitT(FASTApi *rthis, fast_tag_t tag, T data) {

	if ((rthis->get_tag_type(tag) == eFAST_TYPE_I32
			|| rthis->get_tag_type(tag)	== eFAST_TYPE_U32
			|| rthis->get_tag_type(tag) == eFAST_TYPE_I64
			|| rthis->get_tag_type(tag) == eFAST_TYPE_U64
			|| rthis->get_tag_type(tag) == eFAST_TYPE_ASCII
			|| rthis->get_tag_type(tag) == eFAST_TYPE_BYTEVECTOR
			|| rthis->get_tag_type(tag) == eFAST_TYPE_UNICODE)
			&& rthis->get_tag_required(tag) == false && data > -1) {
		data++;
	}

	int size = rthis->toSize(data);

	// Check buffer space once, not for every byte
	if (rthis->check_message_available(FUNCTION, tag, size) < 0)
		return -1;

	if (size <= sizeof(T) + 1 && size >= 1) {
		unsigned int p = 0;
		unsigned int mfactor = p * 7;
		for (p = size - 1; p > 0; p--) {
			mfactor = p * 7;
			rthis->emit_byte((data >> mfactor) & VALUE_BIT);
		}
		rthis->emit_byte((data & VALUE_BIT) | STOP_BIT);

		//rthis->emit_byte ((data >> (size - 1)*7) & VALUE_BIT| ((size == 1)?STOP_BIT:0));
		//rthis->emit_byte ((data >> (p - 1)*7) & VALUE_BIT| ((p == 1)?STOP_BIT:0));
		return size;
	} else {
		return rthis->set_error(FUNCTION, tag, eFAST_ERR_SIZE,
				"size=%d data=%d (%08x)", size, data, data);
	}
}

int FASTApi::emit_double(fast_tag_t tag, i32 iExponent, i64 iMantissa, bool forceModify) {

	if (get_tag_required(tag) == false && iExponent > -1)
		iExponent++;
	int size = 0;
	size += emitT(const_cast<FASTApi*> (this), tag, iExponent);
	size += emitT(const_cast<FASTApi*> (this), tag, (i64) iMantissa);
	return 1;
}

//! @brief Serialize an ASCII string field
int FASTApi::emit_string(fast_tag_t tag, u8* data, int size, bool forceModify) {
	// Zero length strings cannot be encoded
	if (size <= 0)
		return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
				"size=%d (>0 expected)", size);

	// Check buffer space once, not for every byte
	if (check_message_available(FUNCTION, tag, size) < 0)
		return -1;

	{
		int p1;

		for (p1 = 0; p1 < size - 1; p1++)
			emit_byte(data[p1]);

		emit_byte(data[p1] | STOP_BIT); // Last byte
	}

	return size;
}

//////////////////////////////////////////////////////////////////////

//! @brief Deserialize an SBIT coded field

int FASTApi::parse_bytes(u8* data, int size) {
	int p1;

	// Fixme: Filler may be other than space?
	memset(data, 0, size);

	for (p1 = 0; p1 < size; p1++) {
		u8 temp;

		FASTBuffer* input = &p_codec->input;

		if (empty_buffer_pointer(input)) {
			int bytes;

			//if (p_codec->skip_io == 0)
			//   return -1;

			bytes = read(input->fd, input->data, get_buffer_size(input));

			if (bytes <= 0) {
				if (bytes < 0)
					fprintf(stderr, "read failed: %s\n", strerror(errno));

				return -1;
			}

			input->head = input->data;
			input->tail = input->head + bytes;

		}
		temp = *input->head++;

		data[p1] = temp;

		if (temp >= STOP_BIT) {
			data[p1] &= VALUE_BIT;
			return p1 + 1;
		}
	}

	// Fixme: calculate the real number of bytes in the field.

	return set_error(FUNCTION, -1, eFAST_ERR_SIZE,
			"parse buffer overflow size=%d", size);
}

int FASTApi::parse_presence_map() {
	FASTPMap* pmap = &p_codec->pmap;

	u8 data[MAX_PMAP_BYTES];

	int bytes = parse_bytes(data, MAX_PMAP_BYTES);
	if (data[bytes - 1] == 0 && bytes != 1)
		throw "Overlong PMAP value"; //Ignore for the
	int offset;
	int p1;

	if (bytes < 0)
		return bytes;

	for (p1 = 0, offset = 0; p1 < bytes; p1++, offset += 7) {
		u32 temp = data[p1];
		int p2;

		for (p2 = 0; p2 < 7; p2++) {
			pmap->bits[offset + p2] = (temp & bitMask[p2]) != 0;
		}
	}

	p_codec->pmap.size = 7 * bytes;
	return bytes;
}

template<typename T>
int parseT(FASTApi *rthis, fast_tag_t tag, T* data, int sign_mask) {
	u8 buffer[5];
	int bytes;
	int p1;
	T temp;

	bytes = rthis->parse_bytes(buffer, sizeof(buffer));

	if (bytes < 0)
		return bytes;

	temp = 0 - ((buffer[0] & sign_mask) != 0);

	for (p1 = 0; p1 < bytes; p1++)
		temp = (temp << 7) | buffer[p1];

	if (bytes == 1 && temp == 0 && rthis->get_tag_required(tag) == false)
		return -1; //NA optional

	if ((rthis->get_tag_type(tag) == eFAST_TYPE_GROUP || rthis->get_tag_type(
			tag) == eFAST_TYPE_I32 || rthis->get_tag_type(tag)
			== eFAST_TYPE_I64 || rthis->get_tag_type(tag) == eFAST_TYPE_ASCII
			|| rthis->get_tag_type(tag) == eFAST_TYPE_UNICODE
			|| rthis->get_tag_type(tag) == eFAST_TYPE_BYTEVECTOR
			|| rthis->get_tag_type(tag) == eFAST_TYPE_GROUP
			|| rthis->get_tag_type(tag) == eFAST_TYPE_U64
			|| rthis->get_tag_type(tag) == eFAST_TYPE_U32)
			&& rthis->get_tag_required(tag) == false && temp > 0) {
		temp--;

	}

	*data = temp;
	return bytes;

}

void FASTApi::get_double_exponent_mantissa( double data, i32 &iExponent, i64 &iMantissa )
{
	char buff[100];
	if ( data  == 0.0 )
	{
		return;
	}
	sprintf(buff, "%f\0", data);
	get_double_exponent_mantissa( buff, iExponent, iMantissa );
	return;

/*
 * Optimized version will do it later
 * double value = data;
	iExponent = 0;
	iMantissa = 0;
	while (value - double((long) value) != 0.0)
	{
		value = (double) value * pow(10.0, 1.0);
		iExponent -= 1;
	}
	iMantissa = (long)value;
	while (iMantissa % 10 == 0)
	{
		iMantissa /= 10;
		iExponent += 1;
	}*/
}

void FASTApi::get_double_exponent_mantissa(const std::string & value, i32 &iExponent, i64 &iMantissa )
{
	std::string str = value;

	size_t dotPos = str.find(".");
	std::string wholeString = str.substr(0, dotPos);
	std::string fracString;
	if (dotPos != std::string::npos) {
		fracString = str.substr(dotPos + 1);
	}
	std::string mantissaString = wholeString + fracString;
	iExponent = -(fracString.size());
	iMantissa = atoi(mantissaString.c_str());

	//Remove Extra Zeros
	while (iMantissa != 0 && iMantissa % 10 == 0 && iExponent < 64) {
		iMantissa /= 10;
		iExponent += 1;
	}
	if (iExponent > 64) {
		throw("[ERR R1]Decimal Exponent overflow.");
	}
	if (iExponent < -64) {
		throw("[ERR R1]Decimal Exponent undeflow.");
	}
	return;
}

//! @brief Ddouble 
int FASTApi::parse_double(fast_tag_t tag, double* data) {
	i32 iExponent = 0;
	i64 iMantissa = 0;
	int bytes = 0;

	bytes = parseT(this, tag, (i32*) &iExponent, SIGN_MASK_I32);
	if (bytes < 0 && get_tag_required(tag) == false)
		return -1;

	if (bytes == 1 && iExponent == 0 && get_tag_required(tag) == false)
		return -1; //optional

	if (get_tag_required(tag) == false && iExponent > 0)
		iExponent--;

	bytes += parseT(this, tag, (i64*) &iMantissa, SIGN_MASK_I64);

	i32 iPrevExponent = 0;
	i64 iPrevMantissa = 0;

	get_double_exponent_mantissa(*data, iPrevExponent, iPrevMantissa);

	*data = (iMantissa+iPrevMantissa) * pow((double) 10, (iExponent+iPrevExponent));
	return bytes;

}

//! @brief Deserialize an ASCII string

int FASTApi::parse_string(fast_tag_t tag, u8* data, int size) {
	unsigned int bytes = parse_bytes(data, size);
	if (bytes > 0) {
		if (data[0] == 0x00 && get_tag_required(tag) == false) {
			bytes = -1;
		}
	}
	return bytes;
}

//////////////////////////////////////////////////////////////////////
// Decoder functions

//! @brief Start decoding a message
//! Parse the presence map

int FASTApi::decode_message(fast_tag_t tag) {
	if (check_codec(FUNCTION) < 0)
		return -1;

	return parse_presence_map();
}

//! @brief Finish decoding a message

int FASTApi::decode_message_end(fast_tag_t tag) {
	if (check_codec(FUNCTION) < 0)
		return -1;

	return 0;
}

//////////////////////////////////////////////////////////////////////

//! @brief Decode an ASCII string
/*
 EntityValue		Nullable		Description
 0x00							Empty string
 0x00 0x00						“\0”
 0x00 0x41						“A”, Overlong
 0x00			Yes				NULL
 0x00 0x00		Yes				Empty String
 0x00 0x41		Yes				“A”, Overlong
 0x00 0x00 0x00	Yes				“\0”
 0x00 0x00 0x41	Yes				“A”, Overlong
 */

i32 FASTApi::decode_message_length(fast_tag_t tag, u32 value, u8* pbuffer) {
	int size = toSize(value);

	switch (size) {
	// Shifts are logical (unsigned)
	case 5:
		*pbuffer++ = (value >> 28);
	case 4:
		*pbuffer++ = ((value >> 21) & VALUE_BIT);
	case 3:
		*pbuffer++ = ((value >> 14) & VALUE_BIT);
	case 2:
		*pbuffer++ = ((value >> 7) & VALUE_BIT);
	case 1:
		*pbuffer++ = ((value & VALUE_BIT) | STOP_BIT);
		return size;
	default:
		return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
				"size=%d data=%d (%08x)", size, value, value);
	}
}

template<typename T, typename Q, typename S>
int decodeT(FASTApi *rthis, fast_tag_t tag, T* value,
		eFAST_SIGN_MASK mask_type, eFAST_SIGN_MASK mask_type_second,
		eFAST_TYPE fast_type, eFAST_TYPE fast_type_second) {
	eFAST_OP op = static_cast<eFAST_OP> (rthis->get_tag_operator(tag));

	T temp = 0;
	int bytes;

	if (rthis->check_codec(FUNCTION) < 0)
		return -1;

	if (rthis->check_type(FUNCTION, tag, fast_type) < 0)
		return -1;

	switch (op) {
	case eFAST_OP_NONE: // Value must be present
		if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
			bytes = parseT(rthis, tag, (Q*) &temp, mask_type);

			if (bytes > 0) {
				rthis->set(tag, temp);
				break;
			}

			if (bytes < 0) // End of input
				return -1;
		}
		return rthis->value_error(FUNCTION, tag);

	case eFAST_OP_CONST: // Value must not be present
		if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
			rthis->get(tag, *value);
		} else {
			*value = 0;
		}
		return 0;
		break;

	case eFAST_OP_DEFAULT: // Use previous value if not present
		if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
			bytes = parseT(rthis, tag, (Q*) &temp, mask_type);

			if (bytes < 0) // End of input
				return -1;
			*value = temp;
		} else {
			rthis->get(tag, *value);
		}
		return 0;
		break;

	case eFAST_OP_COPY: // Use previous value if not present
		if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
			bytes = parseT(rthis, tag, (Q*) &temp, mask_type);

			if (bytes < 0) // End of input
				return -1;

			rthis->set(tag, temp);
		}
		break;

	case eFAST_OP_INCR: // Use previous value plus one if not present
		if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
			bytes = parseT(rthis, tag, (Q*) &temp, mask_type);

			if (bytes < 0) // End of input
				return -1;
		} else {
			// Rule: The previous value must be valid
			if (rthis->is_valid(tag) == 0)
				return rthis->value_error(FUNCTION, tag);

			rthis->get(tag, temp);
			temp += 1;

		}
		rthis->set(tag, (T) temp);
		break;

	case eFAST_OP_DELTA:
		if (rthis->is_valid(tag)) {
			// Rule: delta defaults to ZERO if there is
			// no value present

			if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
				Q delta;

				bytes = parseT(rthis, tag, (Q*) &delta, mask_type_second);

				if (bytes < 0) // error state already set
					return -1;

				if (bytes > 0) {
					rthis->get(tag, temp);
					temp += delta;

					rthis->set(tag, temp);
				}
			}
		} else {
			// Rule: A field must be present it the
			// current value isn't valid

			if (!rthis->need_presence_map(tag) || rthis->get_presence_pap(tag)) {
				bytes = parseT(rthis, tag, (Q*) &temp, mask_type_second);

				if (bytes < 0) // error state already set
					return -1;

				if (bytes == 0)
					return rthis->value_error(FUNCTION, tag);

				rthis->set(tag, temp);
			}
		}
		break;

	default:
		return rthis->bad_operator_error(FUNCTION, tag);
	}

	rthis->get(tag, *value);
	return 0;
}

int FASTApi::decode_group_count(fast_tag_t tag, u32* value) {
	return decodeT<u32, u32, i32> (this, tag, value, SIGN_MASK_U32,
			SIGN_MASK_I32, eFAST_TYPE_GROUP, eFAST_TYPE_I32);
}

int FASTApi::decode_i32(fast_tag_t tag, i32* value) {
	return decodeT<i32, u32, i32> (this, tag, value, SIGN_MASK_I32,
			SIGN_MASK_I32, eFAST_TYPE_I32, eFAST_TYPE_I32);
}

int FASTApi::decode_u32(fast_tag_t tag, u32* value) {
	return decodeT<u32, u32, i32> (this, tag, value, SIGN_MASK_U32,
			SIGN_MASK_I32, eFAST_TYPE_U32, eFAST_TYPE_I32);
}

int FASTApi::decode_i64(fast_tag_t tag, i64* value) {
	return decodeT<i64, u64, i64> (this, tag, value, SIGN_MASK_I64,
			SIGN_MASK_I64, eFAST_TYPE_I64, eFAST_TYPE_I64);
}

int FASTApi::decode_u64(fast_tag_t tag, u64* value) {
	return decodeT<u64, u64, i64> (this, tag, value, SIGN_MASK_U64,
			SIGN_MASK_I64, eFAST_TYPE_U64, eFAST_TYPE_I64);
}
//////////////////////////////////////////////////////////////////////
//! @brief Decode a 32-bit unsigned integer

int FASTApi::decode_double(fast_tag_t tag, double* value) {
	eFAST_OP op = static_cast<eFAST_OP> (get_tag_operator(tag));

	double temp = 0;
	int bytes;

	if (check_codec(FUNCTION) < 0)
		return -1;

	if (check_type(FUNCTION, tag, eFAST_TYPE_DECMIAL) < 0)
		return -1;

	switch (op) {
	case eFAST_OP_NONE: // Value must be present
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bytes = parse_double(tag, &temp);

			if (bytes > 0) {
				set(tag, temp);
				break;
			}

			if (bytes < 0) // End of input
				return -1;
		}
		return value_error(FUNCTION, tag);

	case eFAST_OP_CONST: // Value must not be present
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			*value = get_double(tag);
		} else {
			*value = 0;
		}
		return 0;
		break;

	case eFAST_OP_DEFAULT: // Use previous value if not present
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bytes = parse_double(tag, &temp);

			if (bytes < 0) // End of input
				return -1;
			*value = temp;
		} else {
			*value = get_double(tag);
		}
		return 0;

	case eFAST_OP_COPY: // Use previous value if not present
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bytes = parse_double(tag, &temp);

			if (bytes < 0) // End of input
				return -1;

			set(tag, temp);
		}
		break;

	case eFAST_OP_DELTA:
		if (is_valid(tag)) {
			if (!need_presence_map(tag) || get_presence_pap(tag)) {

				temp = get_double(tag);
				bytes = parse_double(tag, &temp);

				if (bytes < 0) // NA
					return -1;

				if (bytes > 0) {
					set(tag, temp);
				}
			}
		} else {
			if (!need_presence_map(tag) || get_presence_pap(tag)) {
				bytes = parse_double(tag, &temp);

				if (bytes < 0) // NA
					return -1;

				if (bytes == 0)
					return value_error(FUNCTION, tag);

				set(tag, temp);
			}
		}
		break;

	case eFAST_OP_INCR:
	default:
		return bad_operator_error(FUNCTION, tag);
	}

	*value = get_double(tag);
	return 0;
}
//////////////////////////////////////////////////////////////////////

// flush_group moves the current pmap and body data to the output
// buffer.
// Currently, flush_group is only called from flush_message, but will
// also be called from other sites when repeating groups become
// supported in this implementation. 


//! @brief Flush a group of fields
//! Emit the presence map for the group and the serialized field data
//
//! @param codec

int FASTApi::decode_string(fast_tag_t tag, u8* data, int size) {
	eFAST_OP op = static_cast<eFAST_OP> (get_tag_operator(tag));

	i32 bytes = -1;

	if (check_codec(FUNCTION) < 0)
		return -1;

	if (check_type(FUNCTION, tag, eFAST_TYPE_ASCII) < 0)
		return -1;

#if 0
	// Fixme: Remove arbitrary limit
	if (size <= 0 || size >= 1024)
	return set_error (FUNCTION, tag, eFAST_ERR_SIZE,
			"size=%d (1-1023 expected)", size);
#endif

	switch (op) {
	case eFAST_OP_NONE:
		if (!need_presence_map(tag) || get_presence_pap(tag)) {

			bytes = parse_string(tag, data, size);

			if (bytes < 0) // End of input
				return -1;

			if (bytes == 0)
				return value_error(FUNCTION, tag);
		}
		break;

	case eFAST_OP_CONST:
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			memcpy(data, get_string(tag), size);
		} else {
			*data = 0;
			size = 0;
		}
		return size;
		break;

	case eFAST_OP_DEFAULT:
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bytes = parse_string(tag, data, size);
			if (bytes < 0)
				return -1;
			if (bytes > 0) {
				// Do not set current value as it is default operator
				//set (tag, data, size);
			} else {
				// Copy current value
				memcpy(data, get_string(tag), size);
			}
		} else {
			// Copy current value
			memcpy(data, get_string(tag), size);
		}
		return bytes;
		break;

	case eFAST_OP_COPY:
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bytes = parse_string(tag, data, size);
			if (bytes < 0)
				return -1;

			if (bytes > 0) {
				// Set current value
				set(tag, data, size);
			} else {
				// Copy current value
				memcpy(data, get_string(tag), size);
			}
		} else {
			// Copy current value
			if (*get_valid_value(tag) != 0)
				memcpy(data, get_string(tag), size);
		}
		return 0;
		break;
	case eFAST_OP_DELTA:
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			bool front = false;
			u8 buffer[1024];
			u8* curr = get_string(tag);

			i32 offset = 0;

			bytes = parseT(this, tag, (u32*) &offset, SIGN_MASK_I32);

			if (bytes < 0) // Incorrect size
				return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
						"undersize value %d > size=%d", bytes, size);

			bytes = parse_string(tag, buffer, size);

			if (offset < 0) {
				offset++;
				offset *= -1;
				front = true;
			}

			if (buffer[0] == 0) {
				memcpy(data, get_string(tag), size);
				return 0;
			}

			if (is_valid(tag)) {
				size_t temp = strlen((char*) curr);

				if (bytes > size || offset > temp)
					return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
							"oversized value %d > size=%d", bytes, size);

				// This implementation requires delta string field values
				// to be exactly the size of the field.

				if (temp - abs(offset) + bytes > size)
					return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
							"size too big %d != %d",
							temp - abs(offset) + bytes, size);

				if (front) {
					std::vector<char> backup(temp);
					memcpy(&backup[0], curr, temp);
					memcpy(curr, buffer, bytes);
					memcpy(curr + bytes, &backup[0] + abs(offset), temp - abs(
							offset));
					curr[bytes + temp - abs(offset)] = '\0';
				} else {
					memcpy(curr + temp - offset, buffer, bytes);
					curr[temp - offset + bytes] = '\0';
				}
			} else {
				set(tag, buffer, bytes);
			}
			memcpy(data, get_string(tag), size);
		} else {
			return set_error(FUNCTION, tag, eFAST_ERR_CALL_SEQ,
					"delta does not have PMap bit value %d > size=%d", bytes,
					size);
		}
		break;

	case eFAST_OP_TAIL: // Use previous value plus one if not present
		if (!need_presence_map(tag) || get_presence_pap(tag)) {
			u8 buffer[1024];
			u8* curr = get_string(tag);

			i32 offset = 0;

			bytes = parse_string(tag, buffer, size);

			if (bytes < 0) // Incorrect size
				return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
						"undersize value %d > size=%d", bytes, size);

			if (buffer[0] == 0) {
				memcpy(data, get_string(tag), size);
				return 0;
			}

			if (is_valid(tag)) {
				if (bytes > (u32) size)
					return set_error(FUNCTION, tag, eFAST_ERR_SIZE,
							"oversized value %d > size=%d", bytes, size);

				size_t temp = strlen((char*) curr);
				if (bytes > 0) {
					if (bytes > temp) {
						set(tag, buffer, bytes);
					} else {
						memcpy(curr + temp - bytes, buffer, bytes); //keeping the same size
						curr[temp] = '\0';
					}
				}
			} else {
				set(tag, buffer, bytes);
			}
			memcpy(data, get_string(tag), size);
		} else //Mandatory case and page 16/44
		{
			if (is_valid(tag)) {
				memcpy(data, get_string(tag), size);
				bytes = 0;
			} else {
				if (get_tag_required(tag) == true)
					return set_error(FUNCTION, tag, eFAST_ERR_VALUE,
							"empty value %d > size=%d", bytes, size);
				else
					bytes = -1;
			}
		}
		return bytes;
		break;

	case eFAST_OP_INCR: //NA
	default:
		bad_operator_error(FUNCTION, tag);
	}
	return 0;
}

int FASTApi::flush_group() {
	FASTBuffer* output = &p_codec->output;
	FASTBuffer* msg = &p_codec->msg;

	int map_size;

	//! @remark Make sure there is room in the output buffer

	int size = get_buffer_used(msg);
	int need = MAX_PMAP_BYTES + size;

	if (get_buffer_left(output) <= need)
		return set_error(FUNCTION, -1, eFAST_ERR_SIZE,
				"output buffer overflow left=%d need=%d", get_buffer_left(
						output), need);

	//! Copy the pmap into the output buffer
	map_size = emit_presence_map(&p_codec->pmap, output->tail);

	output->tail += map_size;

	//! Copy the message body into the output buffer
	memcpy(output->tail, msg->head, size);

	output->tail += size;

	//! Reset the presence map
	reset_presence_map();

	reset_buffer(&p_codec->msg);
	return 0;
}

//! @brief Flush a message
//! Flush the last group of the message and write serialized data

int FASTApi::flush_message() {
	flush_group();

	return 0;
}

//////////////////////////////////////////////////////////////////////

// encode_message is called to setup encoding for a message.
// It must not be called again without calling encode_message_end
// in between.

//! @brief Start encoding a new message

int FASTApi::encode_message(fast_tag_t tag) {
	check_codec(FUNCTION);

	if (p_codec->in_message != 0)
		return set_error(FUNCTION, tag, eFAST_ERR_CALL_SEQ,
				"already in a message");

	p_codec->in_message = 1;
	p_codec->curr_tag = tag;
	reset_buffer(&p_codec->output);

	return 0;
}

//! @brief Finish encoding a message

int FASTApi::encode_message_end(fast_tag_t tag) {
	check_codec(FUNCTION);

	if (p_codec->in_message == 0)
		return set_error(FUNCTION, tag, eFAST_ERR_CALL_SEQ, "not in a message");

	p_codec->in_message = 0;
	return flush_message();
}

template<typename T, typename Q>
int encodeT(FASTApi *rthis, fast_tag_t tag, T value, eFAST_TYPE fast_type) {
	eFAST_OP op = static_cast<eFAST_OP> (rthis->get_tag_operator(tag));
	T next = value;

	if (rthis->check_codec(FUNCTION) < 0)
		return -1;

	if (rthis->check_type(FUNCTION, tag, fast_type) < 0)
		return -1;

	switch (op) {
	case eFAST_OP_NONE: // Always emit value
		break;

	case eFAST_OP_CONST: // Never emit value
		T delta;
		rthis->get(tag, delta);
		if (delta == value)
			rthis->set_presence_map(tag, ePMAP_BIT_ON);
		else
			rthis->set_presence_map(tag, ePMAP_BIT_OFF);
		rthis->set(tag, value);
		return 0;
		break;

	case eFAST_OP_DEFAULT: // Emit value if not equal to previous value
		if (rthis->is_equal(tag, value)) {
			rthis->set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}
		rthis->set_presence_map(tag, ePMAP_BIT_ON);
		return emitT(const_cast<FASTApi*> (rthis), tag, value);
		break;

	case eFAST_OP_COPY: // Emit value if not equal to previous value
		if (rthis->is_equal(tag, value)) {
			rthis->set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}
		break;

	case eFAST_OP_INCR: // Emit value if not previous value plus one
		next = value + 1;
		if (rthis->is_equal(tag, value)) {
			rthis->set_presence_map(tag, ePMAP_BIT_OFF);
			rthis->set(tag, value);
			return 0;
		}
		break;

	case eFAST_OP_DELTA:
		if (rthis->is_valid(tag)) // Emit value if not equal to previous
		{
			T curr;
			rthis->get(tag, curr);
			Q delta = value - curr;
			rthis->set_presence_map(tag, ePMAP_BIT_ON);
			rthis->set(tag, value);
			return emitT(const_cast<FASTApi*> (rthis), tag, delta);
		}
		break;

	case eFAST_OP_TAIL: // Fixme: Implement
	default:
		return rthis->bad_operator_error(FUNCTION, tag);
	}

	rthis->set_presence_map(tag, ePMAP_BIT_ON);
	rthis->set(tag, next); // update previous value state
	return emitT(const_cast<FASTApi*> (rthis), tag, value);
}

int FASTApi::encode_group_count(fast_tag_t tag, u32 value) {
	return encodeT<u32, i32> (this, tag, value, eFAST_TYPE_GROUP);
}

int FASTApi::encode_i32(fast_tag_t tag, i32 value) {
	return encodeT<i32, i32> (this, tag, value, eFAST_TYPE_I32);
}

int FASTApi::encode_u32(fast_tag_t tag, u32 value) {
	return encodeT<u32, i32> (this, tag, value, eFAST_TYPE_U32);
}

int FASTApi::encode_i64(fast_tag_t tag, i64 value) {
	return encodeT<i64, i64> (this, tag, value, eFAST_TYPE_I64);
}

int FASTApi::encode_u64(fast_tag_t tag, u64 value) {
	return encodeT<u64, i64> (this, tag, value, eFAST_TYPE_U64);
}

int FASTApi::encode_double(fast_tag_t tag, double value) {
	eFAST_OP op = static_cast<eFAST_OP> (get_tag_operator(tag));

	if (check_codec(FUNCTION) < 0)
		return -1;

	if (check_type(FUNCTION, tag, eFAST_TYPE_DECMIAL) < 0)
		return -1;

	i64 iMantissa = 0;
	i32 iExponent = 0;

	switch (op) {
	case eFAST_OP_NONE: // Always emit value
		break;

	case eFAST_OP_CONST: // Never emit value
		if (get_double(tag) == value)
			set_presence_map(tag, ePMAP_BIT_ON);
		else
			set_presence_map(tag, ePMAP_BIT_OFF);
		set(tag, value);
		return 0;
		break;

	case eFAST_OP_DEFAULT: // Emit value if not equal to previous value
		if (value == get_double(tag)) {
			set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}
		get_double_exponent_mantissa(value, iExponent, iMantissa);
		set_presence_map(tag, ePMAP_BIT_ON);
		return emit_double(tag, iExponent, iMantissa);
		break;

	case eFAST_OP_COPY: // Emit value if not equal to previous value
		if (value == get_double(tag)) {
			set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}
		break;

	case eFAST_OP_DELTA:
		if (is_valid(tag)) // Emit value if not equal to previous
		{
			i64 iPrevMantissa = 0;
			i32 iPrevExponent = 0;
			get_double_exponent_mantissa(get_double(tag), iPrevExponent, iPrevMantissa);

			get_double_exponent_mantissa(value, iExponent, iMantissa);

			iExponent -= iPrevExponent;
			iMantissa -= iPrevMantissa;

			set_presence_map(tag, ePMAP_BIT_ON);
			set(tag, value);

			return emit_double(tag, iExponent, iMantissa);
		}
		break;

	case eFAST_OP_INCR: // NA
	case eFAST_OP_TAIL: // NA
	default:
		return bad_operator_error(FUNCTION, tag);
	}
	set_presence_map(tag, ePMAP_BIT_ON);
	set(tag, value);
	iMantissa = 0;
	iExponent = 0;
	get_double_exponent_mantissa(value, iExponent, iMantissa);
	return emit_double(tag, iExponent, iMantissa);
}

//! @brief Encode a 64-bit unsigned integer
//////////////////////////////////////////////////////////////////////

//! @brief Create a CODEC (encoder/decoder)

int FASTApi::encode_string(fast_tag_t tag, u8* data, int size) {
	eFAST_OP op = static_cast<eFAST_OP> (get_tag_operator(tag));
	size_t len = 0;

	if (check_codec(FUNCTION) < 0)
		return -1;

	if (check_type(FUNCTION, tag, eFAST_TYPE_ASCII) < 0)
		return -1;

	u8* curr = NULL;
	u32 offset = 0;

	// Note: We don't prune the string in the codec layer
	// Pruning should be done by the app codec

	switch (op) {
	case eFAST_OP_NONE:
		break; // Always emit value

	case eFAST_OP_CONST: // Never emit value
		if (is_equal(tag, data, size)) {
			set_presence_map(tag, ePMAP_BIT_ON);
		} else {
			set_presence_map(tag, ePMAP_BIT_OFF);
		}
		set(tag, data, size);
		return 0;
		break;

	case eFAST_OP_DEFAULT: // Emit value if not equal to previous value
		if (is_equal(tag, data, size)) {
			set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}
		set_presence_map(tag, ePMAP_BIT_ON);
		return emit_string(tag, data, size);
		break;

	case eFAST_OP_COPY: // Emit value if not equal to previous value
		if (is_equal(tag, data, size)) {
			set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}

		break;

	case eFAST_OP_DELTA:
		curr = get_string(tag);
		len = strlen((char*) curr);
		offset = find_char_delta_offset(curr, data, size);

		if (offset == size) {
			set_presence_map(tag, ePMAP_BIT_OFF);
			return 0;
		}

		memcpy(curr + offset, data + offset, size - offset);
		curr[size] = '\0';

		data += offset;
		size -= offset;
		len -= offset;

		set_presence_map(tag, ePMAP_BIT_ON);
		emitT(const_cast<FASTApi*> (this), tag, (i32) len); //Required to set the sign
		return emit_string(tag, data, size, false);
		break;

	case eFAST_OP_TAIL: // Fixme: Implement
		curr = get_string(tag);
		offset = find_char_delta_offset(curr, data, size);

		if (offset == size) {
			set_presence_map(tag, ePMAP_BIT_OFF); //No need; it is same
			return 0;
		}
		set_presence_map(tag, ePMAP_BIT_ON);
		memcpy(curr + offset, data + offset, size - offset);
		return emit_string(tag, data, size - offset, false);
		break;
	case eFAST_OP_INCR: // Not defined in the spec
	default:
		return bad_operator_error(FUNCTION, tag);
	}

	set_presence_map(tag, ePMAP_BIT_ON);
	set(tag, data, size);
	return emit_string(tag, data, size);
}

int FASTApi::find_char_delta_offset(u8* a, u8* b, int size) {
	int p1;

	for (p1 = 0; p1 < size; p1++)
		if (a[p1] != b[p1])
			break;

	return p1;
}

FASTCodec* FASTApi::create_codec(void) {
	p_codec = reinterpret_cast<FASTCodec*> (malloc(sizeof(*p_codec)));

	if (p_codec == NULL) {
		fprintf(stderr, "error: [%s] malloc failed\n", FUNCTION);
		return NULL;
	}

	memset(p_codec, 0, sizeof(*p_codec));

	p_codec->magic = FAST_CODEC_MAGIC;

	init_buffer(&p_codec->msg, -1);
	init_buffer(&p_codec->input, 0);
	init_buffer(&p_codec->output, 1);

	return p_codec;
}

int FASTApi::destroy_codec() {
	if (p_codec == NULL) {
		fprintf(stderr, "error: [%s] null codec\n", FUNCTION);
		return -1;
	}

	if (p_codec->magic != FAST_CODEC_MAGIC) {
		fprintf(stderr, "error: [%s] bad codec magic number\n", FUNCTION);
		return -1;
	}

	p_codec->magic = 0; // To prevent dangling references
	free(p_codec);
	return 0;
}

u32 FASTApi::ascii_to_u32(u8* data, int size) {
	u32 temp = 0;
	int p1;

	for (p1 = 0; p1 < size; p1++) {
		int chr = data[p1];
		temp = temp * 10 + chr - '0';
	}
	return temp;
}

